package day09;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class MessageQueue {
    private final int capacity;
    private final Object[] messages;
    private int count;
    private int putIndex;
    private int takeIndex;

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    public MessageQueue(int capacity) {
        this.capacity = capacity;
        messages = new Object[capacity];
        count = 0;
        putIndex = 0;
        takeIndex = 0;
    }

    // 生产者方法：向队列中放入消息
    public void put(Object message) throws InterruptedException {
        lock.lock();
        try {
            // 如果队列已满，生产者等待
            while (count == capacity) {
                notFull.await();
            }
            // 将消息放入队列
            messages[putIndex] = message;
            putIndex = (putIndex + 1) % capacity;
            count++;
            // 通知消费者有新消息可以消费
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    // 消费者方法：从队列中取出消息
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            // 如果队列已空，消费者等待
            while (count == 0) {
                notEmpty.await();
            }
            // 从队列中取出消息
            Object message = messages[takeIndex];
            takeIndex = (takeIndex + 1) % capacity;
            count--;
            // 通知生产者有空间可以放入消息
            notFull.signal();
            return message;
        } finally {
            lock.unlock();
        }
    }
}

class Producer implements Runnable {
    private final MessageQueue queue;
    private int messageCount;

    public Producer(MessageQueue queue, int messageCount) {
        this.queue = queue;
        this.messageCount = messageCount;
    }

    @Override
    public void run() {
        for (int i = 0; i < messageCount; i++) {
            try {
                Object message = "Message " + i;
                queue.put(message);
                System.out.println("Producer produced: " + message);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

class Consumer implements Runnable {
    private final MessageQueue queue;
    private int messageCount;

    public Consumer(MessageQueue queue, int messageCount) {
        this.queue = queue;
        this.messageCount = messageCount;
    }

    @Override
    public void run() {
        for (int i = 0; i < messageCount; i++) {
            try {
                Object message = queue.take();
                System.out.println("Consumer consumed: " + message);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

public class ProducerConsumerExample {
    public static void main(String[] args) {
        MessageQueue queue = new MessageQueue(10);
        int numMessages = 20;

        Thread producerThread = new Thread(new Producer(queue, numMessages));
        Thread consumerThread = new Thread(new Consumer(queue, numMessages));

        producerThread.start();
        consumerThread.start();
    }
}
/*
* MessageQueue类
成员变量
capacity：消息队列的容量。
messages：用于存储消息的对象数组，作为消息队列。
count：当前队列中的消息数量。
putIndex：生产者放入消息的索引位置。
takeIndex：消费者取出消息的索引位置。
lock：ReentrantLock对象，用于保护对消息队列的操作。
notFull和notEmpty：Condition对象，分别用于处理队列满和队列空的情况。
put方法
首先获取锁lock。
检查队列是否已满（count == capacity），如果已满，则调用notFull.await()使生产者线程等待，直到队列有空间。
当有空间时，将消息放入队列，更新putIndex和count。
最后调用notEmpty.signal()通知可能在等待消息的消费者线程。
take方法
同样先获取锁lock。
检查队列是否为空（count == 0），如果为空，则调用notEmpty.await()使消费者线程等待，直到队列中有消息。
当有消息时，从队列中取出消息，更新takeIndex和count。
调用notFull.signal()通知可能在等待队列空间的生产者线程。
Producer类
实现Runnable接口。
在run方法中，循环messageCount次，每次生产一条消息（这里简单地用字符串表示消息），调用queue.put方法将消息放入队列，并打印生产的消息。
Consumer类
实现Runnable接口。
在run方法中，循环messageCount次，每次调用queue.take方法从队列中取出一条消息，并打印消费的消息。
ProducerConsumerExample类（主类）
创建一个容量为 10 的MessageQueue对象。
设定要生产和消费的消息数量为 20。
创建生产者线程和消费者线程，并启动它们。
这个示例完整地展示了如何使用ReentrantLock和Condition实现一个简单的生产者 - 消费者模型的消息队列系统，保证了生产者和消费者在合适的条件下等待和运行，实现了消息的有序处理。
* */